home *** CD-ROM | disk | FTP | other *** search
/ Atari Forever 4 / Atari Forever 4.zip / Atari Forever 4.iso / SERIE_AI / AI_027 / CFAT_R3.LZH / archive.lzh / checkfat.c < prev    next >
Text File  |  1996-03-17  |  48KB  |  1,714 lines

  1. /*3456789012345678901234567890123456789012345678*/
  2. /*
  3.  * checkfat.c vom 13.03.1996
  4.  * 
  5.  * Autor:
  6.  * Thomas Binder
  7.  * (binder@rbg.informatik.th-darmstadt.de)
  8.  *
  9.  * Zweck:
  10.  * FAT-Testprogramm für den Aufruf in einer Shell
  11.  * oder (mit kleinem Hilfsprogramm) aus dem Auto-
  12.  * Ordner. Findet falsche Dateilängen, illegale
  13.  * Start- und Folgecluster, Clusterschleifen,
  14.  * Ordnerschleifen, Files mit als defekt
  15.  * markierten Clustern, Cluster mit mehreren
  16.  * Vorgängern, falsche Clustereinträge, verwaiste
  17.  * Cluster und mehrfach belegte Cluster. Ebenso
  18.  * werden alle als defekt markierten Cluster
  19.  * gefunden. Mittels Kommandozeilenoptionen kann
  20.  * die Ausgabe aller Filenamen, zusätzlich die
  21.  * Ausgabe sämtlicher Cluster jedes Files sowie
  22.  * eine detailierte Ergebnis-Ausgabe eingeschaltet
  23.  * werden.
  24.  * Wichtig: Nur für 16-Bit-FATs geeignet!
  25.  *
  26.  * Vielen Dank auch an meine Betatester (in alpha-
  27.  * betischer Reihenfolge):
  28.  * Alexander Clauss, Dirk Klemmt, Rainer Riedl,
  29.  * Michael Schwingen, Uwe Seimet, Manfred Ssykor
  30.  * und Arno Welzel.
  31.  *
  32.  * History:
  33.  * Irgendwann 1993: Erstellung
  34.  * 23.03.1995: Deutliche Verbesserung. CheckFat
  35.  *             findet jetzt auch Clusterschleifen,
  36.  *             illegale Folgecluster und ist auf
  37.  *             die Steuerung der Ausgabe über
  38.  *             Kommandozeilenoptionen vorbereitet.
  39.  * 24.03.1995: CheckFat findet jetzt auch
  40.  *             Ordnerschleifen und Cluster mit
  41.  *             mehreren Vorgängern.
  42.  * 26.03.1995: Da neuere GEMDOS-Versionen wirklich
  43.  *             alle Datencluster belegen können,
  44.  *             werden jetzt Clusternummern von 2
  45.  *             bis numcl + 1 akzeptiert (bisher
  46.  *             nur bis numcl - 1). Außerdem wurden
  47.  *             jetzt die Steuerung per
  48.  *             Kommandozeile integriert und einige
  49.  *             interne Optimierungen vorgenommen.
  50.  * 27.03.1995: Beginn der Kommentierung, dabei
  51.  *             noch kleinere Optimierungen und
  52.  *             Fehlerbeseitigungen.
  53.  * 29.03.1995: "Fehler" in der Meldung bei Start
  54.  *             ohne Parameter ausgebaut (fehlendes
  55.  *             Newline). Außerdem darf die
  56.  *             Laufwerksangabe jetzt auch mehr als
  57.  *             einen Buchstaben lang sein, der
  58.  *             Rest wird einfach ignoriert (damit
  59.  *             kann beispielsweise auch c: oder
  60.  *             c:\ übergeben werden)
  61.  * 31.03.1995: Anpassungen an MiNTLib
  62.  * 24.07.1995: Neue Kommandozeilenoption -h für
  63.  *             Tastendruck vor Programmende.
  64.  * 10.10.1995: CheckFat merkt sich jetzt auch alle
  65.  *             Dateinamen (auf Wunsch auch mit
  66.  *             Pfad, dabei aber weitaus höherer
  67.  *             Speicherbedarf!) und gibt bei
  68.  *             mehrfach belegten Clustern die
  69.  *             Dateien an (besser: Ist beim
  70.  *             Scannen der Files ein Cluster schon
  71.  *             von einer anderen Datei belegt,
  72.  *             gibt CheckFat deren Namen aus. Wenn
  73.  *             ein Cluster also viermal belegt
  74.  *             ist, wird bei drei Dateien der
  75.  *             Hinweis erscheinen, daß der Cluster
  76.  *             bereits von der letzten belegt
  77.  *             ist.) Diese Ausgabe erfolgt nur,
  78.  *             wenn sie per -x (siehe unten)
  79.  *             angefordert wurde!
  80.  *             In diesem Zusammenhang gibt es auch
  81.  *             drei neue Kommandozeilenoptionen:
  82.  *             -x meldet, wie gerade beschrieben,
  83.  *                mehrfach belegte Cluster mit
  84.  *                dem Dateinamen.
  85.  *             -l merkt sich die kompletten Pfade
  86.  *                (normal werden nur die Filenamen
  87.  *                gespeichert).
  88.  *             -a gibt bei mehrfach belegten
  89.  *                Clustern diesen Umstand bei
  90.  *                jedem beteiligten Cluster aus,
  91.  *                während dies sonst nur beim
  92.  *                ersten Mal passiert (die
  93.  *                restlichen Cluster der Datei
  94.  *                sind ja damit zwangsweise
  95.  *                ebenfalls bereits belegt).
  96.  * 11.10.1995: Fehler in work_dir behoben, der zum
  97.  *             Überlauf des scl-Arrays und damit
  98.  *             zur Überschreibung der Variablen
  99.  *             drive führte. Außerdem wird jetzt
  100.  *             zusätzlich geprüft, ob bei der
  101.  *             Rekursion noch genügend Platz im
  102.  *             my_path-Array ist.
  103.  *             Darüberhinaus stimmt jetzt auch der
  104.  *             Returncode bei Fehlerabbruch, und
  105.  *             für die zu speichernden Filenamen
  106.  *             (-x/-l) wird nicht mehr immer ein
  107.  *             Byte zu wenig angefordert.
  108.  *             Sicherheitsüberprüfungen für den
  109.  *             BPB.
  110.  * 12.10.1995: Bei Unterverzeichnissen wird jetzt
  111.  *             geprüft, ob sie als erstes die
  112.  *             Ordner "." und ".." enthalten. Wenn
  113.  *             nicht, wird das Verzeichnis nicht
  114.  *             bearbeitet. Ebenso werden falsche
  115.  *             Einträge für den Startcluster
  116.  *             dieser beiden Pseudoverzeichnisse
  117.  *             gemeldet.
  118.  *             Kleinen Fehler in der BPB-Prüfung
  119.  *             entfernt (der ersten Datencluster
  120.  *             muß hinter dem Wurzelverzeicnis
  121.  *             beginnen, nicht hinter der 2. FAT).
  122.  * 13.10.1995: Freitag, der 13., und trotzdem habe
  123.  *             ich heute erfahren, daß ich meine
  124.  *             letzte Vordiplomsklausur bestanden
  125.  *             habe :)
  126.  *             Unabhängig davon meldet CheckFat
  127.  *             jetzt bei der Schlußanalyse der FAT
  128.  *             auch Cluster, die auf sich selbst
  129.  *             zeigen.
  130.  * 19.10.1995: Compilierung mit Memdebug, um zu
  131.  *             testen, ob alle mallocs/frees in
  132.  *             Ordnung sind. Sind sie zum Glück :)
  133.  * 20.10.1995: Probleme mit Thing und TOSWIN
  134.  *             (Parameter werden nicht erkannt),
  135.  *             daher ein wenig Debug-Output.
  136.  * 26.10.1995: Die Probleme lagen an Thing, der
  137.  *             Debug-Output ist also wieder weg.
  138.  *             Es gibt eine neue Kommandozeilen-
  139.  *             Option -d, mit der die Ausgabe von
  140.  *             als defekt markierten Clustern
  141.  *             eingeschaltet wird. Bisher wurden
  142.  *             sie immer ausgegeben und als Fehler
  143.  *             gemeldet; das hat sich aber als
  144.  *             unbrauchbar erwiesen, weil Cluster
  145.  *             16383 bei größeren Partionen wegen
  146.  *             eines GEMDOS-Fehlers von den
  147.  *             meisten Partitionierungsprogrammen
  148.  *             als defekt markiert wird.
  149.  *             Die Versionsnummer entfernt, weil
  150.  *             sie ohnehin recht wenig aussagt.
  151.  * 29.10.1995: Neue Option -u, bei der CheckFat
  152.  *             nicht mehr Dlock benutzt.
  153.  * 12.12.1995: Bei einem defekten Cluster 16383
  154.  *             erfolgt jetzt die Meldung, daß dies
  155.  *             normalerweise eine Schutzmaßnahme
  156.  *             gegen einen GEMDOS-Fehler ist.
  157.  * 20.12.1995: CheckFat gibt jetzt bei Option -?
  158.  *             die Kurzanleitung aus (also so, wie
  159.  *             wie es auch bei Aufruf komplett
  160.  *             ohne Parameter geschieht).
  161.  * 21.12.1995: In der Kurzanleitung fehlte noch
  162.  *             die gestern eingeführte Option -?.
  163.  * 15.01.1996: Letzte Vorbereitungen für die
  164.  *             Veröffentlichung.
  165.  * 18.02.1996: Neue Option -s für eingeschränkte
  166.  *             "Gesprächigkeit" von CheckFat.
  167.  * 27.02.1996: CheckFat meldet jetzt auch bei als
  168.  *             defekt markiertem Cluster 16384,
  169.  *             daß dies meistens eine Vorsichts-
  170.  *             maßnahme ist.
  171.  * 01.03.1996: CheckFat macht es jetzt wirklich
  172.  *             richtig und meldet die Vorsichts-
  173.  *             maßnahme bei den beiden Clustern,
  174.  *             die an der 32767-Sektoren-Grenze
  175.  *             liegen.
  176.  * 05.03.1996: Vergleich beider FATs und Meldung
  177.  *             eventueller Untschiede. Durch die
  178.  *             neue Option -1 kann man CheckFat
  179.  *             anweisen, bei Unterschieden zum
  180.  *             Test FAT 1 zu benutzen.
  181.  *             Außerdem werden jetzt auch die
  182.  *             Startcluster vor der Überprüfung
  183.  *             auf Korrektheit ausgegeben.
  184.  *             Files mit leerem Cluster sowie
  185.  *             Verweise auf unbelegte Cluster
  186.  *             werden jetzt auch bzw. gesondert
  187.  *             gemeldet.
  188.  *             Neue Option -f zur Ausgabe des
  189.  *             Fragmentierungsgrades.
  190.  * 06.03.1996: Ausführlichere Ausgabe des
  191.  *             Fragmentierungsgrades.
  192.  * 13.03.1996: Die BPB-Überprüfung testet jetzt
  193.  *             auch, ob die Sektor- oder Cluster-
  194.  *             größe 0 ist. Das kann z.B. dann
  195.  *             passieren, wenn man eine Partition
  196.  *             mit mehr als 1 GB erstellt hat.
  197.  *             Sowas sollte ein Partionierer nur
  198.  *             nach einer Sicherheitsabfrage
  199.  *             erlauben, weil es unter GEMDOS
  200.  *             nicht korrekt funktioniert.
  201.  * 17.03.1996: Korrekte Überprüfung der FAT-Größe
  202.  *             im BPB-Test. Außerdem wird jetzt
  203.  *             eine eigene BPB-Struktur benutzt,
  204.  *             die mit vorzeichenlosen Integern
  205.  *             arbeitet (dadurch werden jetzt
  206.  *             Laufwerke mit 32768 Bytes großen
  207.  *             Clustern korrekt geprüft).
  208.  *             Workaround für Partitionen, deren
  209.  *             Cluster 65536 Bytes groß sind (das
  210.  *             sind im BPB 0, da nur 16 Bit-Werte
  211.  *             in der Struktur stehen!) Das GEMDOS
  212.  *             des Falcon kommt damit zwar klar,
  213.  *             ratsam ist es jedoch nicht.
  214.  */
  215.  
  216. #include <stdlib.h>
  217. #include <stdio.h>
  218. #include <string.h>
  219. #include <mintbind.h>
  220. #include <portab.h>
  221. #include <memdebug.h>
  222.  
  223. /* Benötigte Strukturen und Konstanten */
  224.  
  225. #define VERSION   "vom 17.03.1996"
  226.  
  227. #define NOT16BIT  1
  228. #define NOMEM     2
  229. #define READERROR 3
  230. #define NOBPB     4
  231. #define TOODEEP   5
  232. #define LOCKED    6
  233. #define NOBIOS    7
  234. #define WRONGBPB  8
  235. #define TMFILES   9
  236.  
  237. #define WRONG       0
  238. #define ILLEGAL     1
  239. #define LOOPS       2
  240. #define DIRLOOPS    3
  241. #define DESTROYED   4
  242. #define EMPTYCLUST  5
  243. #define AMBIGUOUS   6
  244. #define ILLCL       7
  245. #define DAMAGED     8
  246. #define ORPHAN      9
  247. #define MULTIPLE    10
  248. #define ILLSUBDIR   11
  249. #define ILLPSEUDO   12
  250. #define SELFPOINTER 13
  251. #define EMPTYPOINT  14
  252. #define NO_OF_TYPES 15
  253.  
  254. /*
  255.  * Die maximale Rekursionstiefe bestimmt sich in
  256.  * etwa durch [*_StkSize / (61 + PATHMAX)] - 4,
  257.  * wobei man natürlich eine recht große
  258.  * Sicherheitsreserve für den Stack lassen sollte
  259.  */
  260. #define MAXDEPTH  17
  261.  
  262. /*
  263.  * Bestimmt, wie lang ein Pfad inklusive Nullbyte
  264.  * maximal sein darf. Sollte diese Grenze während
  265.  * der Rekursion innerhalb von work_dir
  266.  * überschritten werden, wird CheckFat mit der
  267.  * Meldung "Zu tiefe Ordnerverschachtelung"
  268.  * abgebrochen.
  269.  */
  270. #define PATHMAX   129
  271.  
  272. #define MAXCLUST  (bpb->numcl + 1)
  273.  
  274. typedef struct
  275. {
  276.   char  dir_name[11];
  277.   BYTE  dir_attr;
  278.   BYTE  dir_dummy[10];
  279.   UWORD dir_time;
  280.   UWORD dir_date;
  281.   UWORD dir_stcl;
  282.   ULONG dir_flen;
  283. } DIR;
  284.  
  285. typedef struct
  286. {
  287.     UWORD   recsiz;
  288.     UWORD   clsiz;
  289.     UWORD   clsizb;
  290.     UWORD   rdlen;
  291.     UWORD   fsiz;
  292.     UWORD   fatrec;
  293.     UWORD   datrec;
  294.     UWORD   numcl;
  295.     UWORD   bflags;
  296. } _BPB_;
  297.  
  298. /* Prototypen */
  299.  
  300. void leave(WORD retcode);
  301. WORD eval_args(WORD argc, char *argv[]);
  302. void usage(void);
  303. void err(WORD code);
  304. void correct_dir(DIR *dir, WORD entries);
  305. void work_dir(DIR *dir, WORD entries, char *path,
  306.   UWORD *scl, WORD depth);
  307. void swap(UWORD *value);
  308. void lswap(ULONG *value);
  309.  
  310. /* Globale Variablen */
  311.  
  312. char  *errtext[] = {
  313.         "Kein Fehler!",
  314.         "Medium hat keine 16-Bit-Fat!",
  315.         "Kein Speicher mehr frei!",
  316.         "Lesefehler!",
  317.         "Bios-Parameterblock unlesbar!",
  318.         "Zu tiefe Ordnerverschachtelung!",
  319.         "Laufwerk gesperrt!",
  320.         "Kein BIOS-Laufwerk!",
  321.         "Bios-Parameterblock fehlerhaft!",
  322.         "Zu viele Files!?"},
  323.       *reporttext[NO_OF_TYPES] = {
  324.         "%d unterschiedliche Dateilänge(n)!\n",
  325.         "%d File(s)/Ordner mit illegalem Start/"
  326.           "Folgecluster!\n",
  327.         "%d File(s)/Ordner mit "
  328.           "Clusterschleife!\n",
  329.         "%d Ordnerschleife(n)!\n",
  330.         "%d File(s)/Ordner mit defektem "
  331.           "Cluster!\n",
  332.         "%d File(s)/Ordner mit leerem Cluster!\n",
  333.         "%d Cluster ohne eindeutigen "
  334.           "Vorgänger!\n",
  335.         "%d illegale(r) Folgecluster!\n",
  336.         "%d als defekt markierte(r) Cluster!\n",
  337.         "%d verwaiste(r) Cluster!\n",
  338.         "%d mehrfach belegte(r) Cluster!\n",
  339.         "%d Verzeichnis(se) ohne '.' und/oder "
  340.           "'..'!\n",
  341.         "%d falsche(r) Startcluster bei '.' "
  342.           "und/oder '..'!\n",
  343.         "%d auf sich selbst zeigende(r) "
  344.           "Cluster!\n",
  345.         "%d Verweis(e) auf leere(n) Cluster!\n"},
  346.       *filenames[32768L];
  347. UWORD filenr,
  348.       fragments,
  349.       fragmented,
  350.       min_dist,
  351.       max_dist,
  352.       *fat,
  353.       startclusters[MAXDEPTH];
  354. WORD  drive,
  355.       to_report[NO_OF_TYPES],
  356.       shownames,    /* Namen anzeigen */
  357.       showclusters, /* Cluster anzeigen */
  358.       verbose,      /* Ausführliche Meldungen */
  359.       crosslinks,   /* Namen bei Crosslinks */
  360.       showall,      /* Alle Crosslinks */
  361.       longnames,    /* Pfadnamen bei Crosslinks */
  362.       showdamaged,  /* Defekte Cluster melden */
  363.       nolock,       /* Device nicht locken */
  364.       silent,       /* Keine Rahmenausgaben */
  365.       usefat1,      /* Bei Bedarf FAT1 benutzen */
  366.       showfrag,     /* Fragmentierung zeigen */
  367.       hold,         /* Auf Tastendruck warten */
  368.       quit,
  369.       entries,
  370.       *checkfat,
  371.       *tempcheck;
  372. LONG  lock,
  373.       clsiz_b;
  374. ULONG distances;
  375. _BPB_ *bpb;
  376. DIR   *rootdir;
  377.  
  378. void main(WORD argc, char *argv[])
  379. {
  380.   UWORD i, j,
  381.         length,
  382.         free_areas,
  383.         free_length,
  384.         min_free,
  385.         max_free,
  386.           critic1,
  387.           critic2,
  388.         badfat,
  389.         *fat2;
  390.   WORD  differ = 0;
  391.   LONG  minfat;
  392.  
  393.   fat = 0L;
  394.   checkfat = tempcheck = 0L;
  395.   rootdir = 0L;
  396.   shownames = showclusters = verbose = quit =
  397.     hold = showall = longnames = showdamaged =
  398.     nolock = usefat1 = showfrag = 0;
  399. /*
  400.  * Kommandozeile auswerten, beenden, wenn
  401.  * fehlerhaft
  402.  */
  403.   if (!eval_args(argc, argv))
  404.     leave(2);
  405. /*
  406.  * Versuchen, das gewünschte Bios-Device gegen
  407.  * GEMDOS-Zugriffe zu schützen. Ist der Aufruf
  408.  * vorhanden und es trat dabei ein Fehler auf,
  409.  * wird CheckFat mit einer entsprechenden Meldung
  410.  * verlassen.
  411.  */
  412.   if (!nolock &&
  413.     ((lock = Dlock(1, drive)) != -32L))
  414.   {
  415.     if (lock == -46L)
  416.       err(NOBIOS);
  417.     if (lock != 0L)
  418.       err(LOCKED);
  419.   }
  420.   for (i = 0; i < NO_OF_TYPES; i++)
  421.     to_report[i] = 0;
  422. /* Versuchen, den Bios-Parameterblock zu lesen */
  423.   if ((bpb = (_BPB_ *)Getbpb(drive)) == 0L)
  424.     err(NOBPB);
  425. /*
  426.  * Sicherstellen, daß es sich um eine 16-Bit-FAT
  427.  * handelt
  428.  */
  429.   if ((bpb->bflags & 1) != 1)
  430.     err(NOT16BIT);
  431. /*
  432.  * Einige Tests, ob es wirklich ein gültiger BPB
  433.  * ist:
  434.  * - Bytes pro Sektor durch 512 teilbar
  435.  * - Bytes pro Sektor nicht Null
  436.  * - Bytes pro Cluster = Sektoren pro Cluster *
  437.  *   Bytes pro Sektor
  438.  * - Wurzelverzeichnis mindestens 1 Sektor lang
  439.  * - mindestens ein, maximal 32766 Datencluster
  440.  * - FAT groß genug für alle Datencluster
  441.  * - Erster Datensektor hinter Ende von Directory
  442.  */
  443.   if (bpb->recsiz % 512)
  444.     err(WRONGBPB);
  445.   if (!bpb->recsiz)
  446.     err(WRONGBPB);
  447. /* Workaround für Cluster mit 65536 Bytes */
  448.   if (((LONG)bpb->recsiz * (LONG)bpb->clsiz) ==
  449.     0x10000L)
  450.   {
  451.     clsiz_b = 0x10000L;
  452.   }
  453.   else
  454.     clsiz_b = bpb->clsizb;
  455.   if (clsiz_b != (bpb->clsiz * bpb->recsiz))
  456.     err(WRONGBPB);
  457.   if (bpb->rdlen < 1)
  458.     err(WRONGBPB);
  459.   if ((bpb->numcl < 1) || (bpb->numcl > 32766))
  460.     err(WRONGBPB);
  461.   minfat = ((LONG)(bpb->numcl + 2) * 2L +
  462.     ((LONG)bpb->recsiz - 1L)) / (LONG)bpb->recsiz;
  463.   if ((WORD)minfat > bpb->fsiz)
  464.     err(WRONGBPB);
  465.   if ((bpb->fatrec + bpb->fsiz + bpb->rdlen) >
  466.     bpb->datrec)
  467.   {
  468.     err(WRONGBPB);
  469.   }
  470. /*
  471.  * Speicherplatz für Wurzelverzeichnis, FAT und
  472.  * zwei Testtabellen anfordern. Gelingt dies
  473.  * nicht, Programm mit Meldung verlassen.
  474.  */
  475.   if ((rootdir = (DIR *)malloc((LONG)bpb->rdlen
  476.     * (LONG)bpb->recsiz)) == 0L)
  477.   {
  478.     err(NOMEM);
  479.   }
  480.   if ((fat = (UWORD *)malloc((LONG)bpb->fsiz *
  481.     (LONG)bpb->recsiz)) == 0L)
  482.   {
  483.     err(NOMEM);
  484.   }
  485.   if ((checkfat = (WORD *)malloc((LONG)bpb->fsiz
  486.     * (LONG)bpb->recsiz)) == 0L)
  487.   {
  488.     err(NOMEM);
  489.   }
  490.   if ((tempcheck = (WORD *)malloc((LONG)
  491.     bpb->fsiz * (LONG)bpb->recsiz)) == 0L)
  492.   {
  493.     err(NOMEM);
  494.   }
  495.   fat2 = (UWORD *)tempcheck;
  496. /*
  497.  * Anzahl der Einträge im Wurzelverzeichnis
  498.  * berechnen
  499.  */
  500.   entries = (WORD)((LONG)bpb->rdlen *
  501.     bpb->recsiz / 32);
  502. /*
  503.  * Die beiden kritischen Cluster berechnen, die
  504.  * bei GEMDOS-Versionen kleiner < 0.30 als defekt
  505.  * markiert werden sollten und daher von CheckFat
  506.  * nur mit Bemerkung gemeldet werden.
  507.  */
  508.   critic1 = 32767U / bpb->clsiz;
  509.   critic2 = critic1 + 1;
  510. /*
  511.  * Wurzelverzeichnis und FATs einlesen. Tritt
  512.  * dabei ein Fehler auf, Programm beenden
  513.  */
  514.   if (Rwabs(0, (void *)rootdir, bpb->rdlen,
  515.     bpb->fatrec + bpb->fsiz, drive))
  516.   {
  517.     err(READERROR);
  518.   }
  519.   if (Rwabs(0, (void *)fat, bpb->fsiz,
  520.     bpb->fatrec, drive))
  521.   {
  522.     err(READERROR);
  523.   }
  524.   if (Rwabs(0, (void *)fat2, bpb->fsiz,
  525.     bpb->fatrec - bpb->fsiz, drive))
  526.   {
  527.     err(READERROR);
  528.   }
  529.   if (!silent)
  530.   {
  531.     printf("CheckFat: Prüfe FAT von Laufwerk "
  532.       "%c...\n", (char)(drive + 65));
  533.   }
  534. /*
  535.  * FAT-Einträge in Motorola-Format wandeln und
  536.  * vergleichen
  537.  */
  538.   for (i = 0; i <= MAXCLUST; i++)
  539.   {
  540.     swap(&fat[i]);
  541.     swap(&fat2[i]);
  542.     if (fat[i] != fat2[i])
  543.     {
  544.       if (verbose)
  545.       {
  546.         printf("Eintrag %u in beiden FATs nicht "
  547.           "identisch!\n", i);
  548.       }
  549.       else if (!differ)
  550.       {
  551.         printf("FATs sind ab Eintrag %u nicht "
  552.           "identisch!\n", i);
  553.       }
  554.       differ = 1;
  555.     }
  556.   }
  557. /*
  558.  * Soll bei Ungleichheit FAT 1 benutzt werden,
  559.  * einfach die betroffenen Pointer tauschen
  560.  */
  561.   if (differ && usefat1)
  562.   {
  563.     tempcheck = (WORD *)fat;
  564.     fat = fat2;
  565.   }
  566.   memset(filenames, 0, sizeof(char *) * 32768L);
  567.   memset(checkfat, 0, (LONG)bpb->fsiz *
  568.     (LONG)bpb->recsiz);
  569.   memset(tempcheck, 0, (LONG)bpb->fsiz *
  570.     (LONG)bpb->recsiz);
  571.   filenr = fragments = fragmented = max_dist = 0;
  572.   min_dist = 0xffffU;
  573.   distances = 0;
  574. /*
  575.  * Wurzelverzeichnis bearbeiten. Da die Funktion
  576.  * work_dir rekursiv arbeitet, wird dadurch der
  577.  * gesamte Dateibaum durchgetestet. Die globale
  578.  * Variable quit wird auf einen Wert != 0 gesetzt,
  579.  * wenn dabei ein Lesefehler oder Speichermangel
  580.  * aufgetreten ist. In diesem Fall wird CheckFat
  581.  * mit einer Meldung beendet.
  582.  */
  583.   work_dir(rootdir, entries, "\\",
  584.     startclusters, 0);
  585.   if (quit)
  586.     err(quit);
  587.   if (!silent)
  588.     puts("\nCheckFat-Ergebnis:");
  589. /* Fragmentierungsgrad ausgeben, wenn gewünscht */
  590.   if (showfrag)
  591.   {
  592.     if (filenr && fragmented)
  593.     {
  594.       printf("%u Prozent der Files/Ordner (%u "
  595.         "von %u) sind fragmentiert!\n",
  596.         (UWORD)(+((ULONG)fragmented * 100UL) /
  597.         (ULONG)filenr), fragmented, filenr);
  598.       if (verbose)
  599.       {
  600.         printf("Clustersprünge insgesamt: %u\n",
  601.           fragments);
  602.         printf("Mittlere Anzahl von Cluster"
  603.           "sprüngen pro fragmentierter/m Datei/"
  604.           "Ordner: %u\n", fragments / fragmented);
  605.         printf("Mittlere Länge eines Sprungs: "
  606.           "%lu Cluster (minimal: %u, maximal: "
  607.           "%u)\n", distances / (ULONG)fragments,
  608.           min_dist, max_dist);
  609.       }
  610.     }
  611.     else
  612.     {
  613.       puts("Es gibt keine fragmentierten Dateien/"
  614.         "Ordner!");
  615.     }
  616.     free_areas = free_length = max_free = 0;
  617.     min_free = 0xffffU;
  618.     for (i = 2; i <= MAXCLUST;) /* Kein Smiley */
  619.     {
  620.       if (fat[i])
  621.       {
  622.         for (j = i + 1; (j <= MAXCLUST) && fat[j];
  623.           j++);
  624.         i = j;
  625.       }
  626.       else
  627.       {
  628.         free_areas++;
  629.         length = 0;
  630.         for (j = i; (j <= MAXCLUST) && !fat[j];
  631.           j++)
  632.         {
  633.           length++;
  634.         }
  635.         if (length < min_free)
  636.           min_free = length;
  637.         if (length > max_free)
  638.           max_free = length;
  639.         free_length += length;
  640.         i = j;
  641.       }
  642.     }
  643.     if (free_areas)
  644.     {
  645.       if (free_areas > 1)
  646.       {
  647.         printf("Der freie Platz ist in %u "
  648.           "Bereiche zerteilt!\n", free_areas);
  649.         if (verbose)
  650.         {
  651.           printf("Durchschnittliche Länge eines "
  652.             "Bereichs: %u Cluster (%lu Bytes)\n",
  653.             free_length / free_areas,
  654.             (ULONG)(free_length / free_areas) *
  655.             (ULONG)clsiz_b);
  656.           printf("Kleinster Bereich: %u Cluster "
  657.             "(%lu Bytes)\n", min_free,
  658.             (ULONG)min_free * (ULONG)clsiz_b);
  659.           printf("Größter Bereich: %u Cluster "
  660.             "(%lu Bytes)\n", max_free,
  661.             (ULONG)max_free * (ULONG)clsiz_b);
  662.         }
  663.       }
  664.       else
  665.       {
  666.         printf("%lu Bytes in %u Cluster(n) sind "
  667.           "an einem Stück frei!\n",
  668.           (ULONG)free_length * (ULONG)clsiz_b,
  669.           free_length);
  670.       }
  671.     }
  672.   }
  673. /*
  674.  * Alle (gültigen) Clustereinträge durchgehen und
  675.  * Fehler melden bzw. merken (ersteres nur, wenn
  676.  * ausführliche Ausgabe per -v gewünscht ist)
  677.  */
  678.   memset(tempcheck, 0, (LONG)bpb->fsiz *
  679.     (LONG)bpb->recsiz);
  680.   for (i = 2; i <= MAXCLUST; i++)
  681.   {
  682. /*
  683.  * Prüfen, ob weitere Cluster auf den Folgecluster
  684.  * des aktuellen zeigen oder ob der Folgecluster
  685.  * nicht belegt ist
  686.  */
  687.     if ((fat[i] >= 2) && (fat[i] <= MAXCLUST))
  688.     {
  689.       if (!fat[fat[i]])
  690.       {
  691.         if (verbose)
  692.         {
  693.           printf("Cluster %u verweist auf einen "
  694.             "unbelegten Cluster!\n", i);
  695.         }
  696.         to_report[EMPTYPOINT]++;
  697.       }
  698.       if (tempcheck[fat[i]] > 0)
  699.       {
  700.         if (verbose)
  701.         {
  702.           printf("Folgende Cluster zeigen alle "
  703.             "auf Cluster %u:\n", fat[i]);
  704.           printf("%u %u ", tempcheck[fat[i]], i);
  705.           for (j = i + 1; j <= MAXCLUST; j++)
  706.           {
  707.             if (fat[j] == fat[i])
  708.               printf("%u ", j);
  709.           }
  710.           puts("");
  711.         }
  712.         to_report[AMBIGUOUS]++;
  713.         tempcheck[fat[i]] = -1;
  714.       }
  715.       else
  716.       {
  717.         if (!tempcheck[fat[i]])
  718.           tempcheck[fat[i]] = i;
  719.       }
  720.     }
  721. /* Prüfen, ob Cluster mehrfach belegt ist */
  722.     if (checkfat[i] > 1)
  723.     {
  724.       if (verbose)
  725.       {
  726.         printf("Cluster %u ist %d-fach belegt!\n",
  727.           i, checkfat[i]);
  728.       }
  729.       to_report[MULTIPLE]++;
  730.     }
  731. /* Prüfen, ob Cluster verwaist ist */
  732.     if ((fat[i] > 1) && ((fat[i] <= MAXCLUST) ||
  733.       (fat[i] == 0xffffU)) && (checkfat[i] == 0))
  734.     {
  735.       if (verbose)
  736.         printf("Cluster %u ist verwaist!\n", i);
  737.       to_report[ORPHAN]++;
  738.     }
  739. /* Prüfen, ob Cluster gültigen Folgecluster hat */
  740.     if (fat[i] && ((fat[i] < 2) ||
  741.       ((fat[i] < 0xfff0U) &&
  742.       (fat[i] > MAXCLUST))))
  743.     {
  744.       if (verbose)
  745.       {
  746.         printf("Cluster %u hat illegalen "
  747.           "Folgecluster!\n", i);
  748.       }
  749.       to_report[ILLCL]++;
  750.     }
  751. /*
  752.  * Prüfen, ob Cluster als defekt markiert ist. Die
  753.  * Ausgabe erfolgt nur, wenn Option -d aktiv ist.
  754.  */
  755.     if (showdamaged && (fat[i] >= 0xfff0U) &&
  756.       (fat[i] <= 0xfff7U))
  757.     {
  758.       if (verbose)
  759.       {
  760.         printf("Cluster %u ist als defekt "
  761.           "markiert!\n", i);
  762.         if ((i == critic1) || (i == critic2))
  763.         {
  764.           puts("Dies ist in der Regel nur ein "
  765.             "Schutz gegen einen GEMDOS-Fehler!");
  766.         }
  767.       }
  768.       to_report[DAMAGED]++;
  769.     }
  770. /* Prüfen, ob Cluster auf sich selbst verweist */
  771.     if (fat[i] == i)
  772.     {
  773.       if (verbose)
  774.       {
  775.         printf("Cluster %u zeigt auf sich "
  776.           "selbst!\n", i);
  777.       }
  778.       to_report[SELFPOINTER]++;
  779.     }
  780.   }
  781. /* Eventuell gefundene Fehler berichten */
  782.   badfat = differ;
  783.   if (differ)
  784.     puts("Unterschiedliche FATs!");
  785.   for (i = 0; i < NO_OF_TYPES; i++)
  786.   {
  787.     badfat += to_report[i];
  788.     if (to_report[i])
  789.       printf(reporttext[i], to_report[i]);
  790.   }
  791.   if (!badfat && !silent)
  792.     puts("Alles OK!");
  793. /*
  794.  * Am Schluß den Speicher und das Laufwerk wieder
  795.  * freigeben und 0 zurückliefern, wenn kein Fehler
  796.  * gefunden wurde, sonst 3
  797.  */
  798.   for (i = 32767U; i > 0; i--)
  799.   {
  800.     if (filenames[i])
  801.       free(filenames[i]);
  802.   }
  803.   if (filenames[i])
  804.     free(filenames[i]);
  805.   free((void *)tempcheck);
  806.   free((void *)checkfat);
  807.   free((void *)fat);
  808.   free((void *)rootdir);
  809.   if (!nolock && (lock == 0L))
  810.     Dlock(0, drive);
  811.   if (badfat)
  812.     leave(3);
  813.   else
  814.     leave(0);
  815. }
  816.  
  817. /*
  818.  * leave
  819.  *
  820.  * Verläßt CheckFat mit einem gegebenen Returncode
  821.  * und wartet dabei gegebenenfalls vorher auf eine
  822.  * Taste.
  823.  *
  824.  * Eingabe:
  825.  * retcode: Zurückzuliefernder Returncode
  826.  */
  827. void leave(WORD retcode)
  828. {
  829.   if (hold)
  830.   {
  831.     if (!silent)
  832.       puts("\nBitte eine Taste drücken!");
  833.     Cnecin();
  834.   }
  835.   exit(retcode);
  836. }
  837.  
  838. /*
  839.  * eval_args
  840.  *
  841.  * Wertet die Kommandozeile aus, die CheckFat
  842.  * übergeben wurde.
  843.  *
  844.  * Eingabe:
  845.  * argc: Anzahl der Elemente in argv
  846.  * argv: Stringarray mit einzelnen Optionen
  847.  *       (argc und argv haben exakt die gleiche
  848.  *       Bedeutung wie bei main!)
  849.  *
  850.  * Rückgabe:
  851.  * 0: Parameter waren fehlerhaft
  852.  * 1: Parameter OK
  853.  */
  854. WORD eval_args(WORD argc, char *argv[])
  855. {
  856.   WORD  i, j;
  857.  
  858.   drive = -1;
  859.   if (argc < 2)
  860.   {
  861.     usage();
  862.     return(0);
  863.   }
  864.   for (i = 1; i < argc; i++)
  865.   {
  866.     if (*argv[i] == '-')
  867.     {
  868.       if (strlen(argv[i]) == 1)
  869.       {
  870.         puts("CheckFat: Falsches Optionsformat "
  871.           "- ignoriert");
  872.         continue;
  873.       }
  874.       for (j = 1; j < strlen(argv[i]); j++)
  875.       {
  876.         switch (argv[i][j])
  877.         {
  878.           case 'v':
  879.             verbose = 1;
  880.             break;
  881.           case 'n':
  882.             shownames = 1;
  883.             break;
  884.           case 'c':
  885.             shownames = showclusters = 1;
  886.             break;
  887.           case 'a':
  888.             showall = 1;
  889.             break;
  890.           case 'x':
  891.             crosslinks = 1;
  892.             break;
  893.           case 'l':
  894.             crosslinks = longnames = 1;
  895.             break;
  896.           case 'd':
  897.             showdamaged = 1;
  898.             break;
  899.           case 'u':
  900.             nolock = 1;
  901.             break;
  902.           case 's':
  903.             silent = 1;
  904.             break;
  905.           case '1':
  906.             usefat1 = 1;
  907.             break;
  908.           case 'f':
  909.             showfrag = 1;
  910.             break;
  911.           case 'h':
  912.             hold = 1;
  913.             break;
  914.           case '?':
  915.             usage();
  916.             return(0);
  917.           default:
  918.             printf("CheckFat: Unbekannte Option "
  919.               "'%c' - ignoriert\n", argv[i][j]);
  920.         }
  921.       }
  922.     }
  923.     else
  924.     {
  925.       drive = (*argv[i] & ~32) - 65;
  926.       if ((drive < 0) || (drive > 31))
  927.       {
  928.         puts("CheckFat: Falsche "
  929.           "Laufwerksangabe!");
  930.         return(0);
  931.       }
  932.       if (i != (argc - 1))
  933.       {
  934.         puts("CheckFat: Überflüssige Parameter "
  935.           "- ignoriert");
  936.       }
  937.       break;
  938.     }
  939.   }
  940.   if (drive == -1)
  941.   {
  942.     puts("CheckFat: Laufwerksangabe fehlt!");
  943.     return(0);
  944.   }
  945.   return(1);
  946. }
  947.  
  948. /*
  949.  * usage
  950.  *
  951.  * Gibt die Kurzanleitung aus.
  952.  */
  953. void usage(void)
  954. {
  955.   puts("CheckFat "VERSION);
  956. #ifdef STCOMPUTER
  957.   puts("(c) 1996 by MAXON Computer GmbH");
  958. #endif
  959.   puts("Geschrieben in Pure C von Thomas Binder");
  960.   puts("\nAufruf: checkfat [Optionen] Laufwerk");
  961.   puts("\nOptionen:");
  962.   puts("\t-v: Ausführliche FAT-Fehlermeldungen");
  963.   puts("\t-n: Alle Filenamen anzeigen");
  964.   puts("\t-c: Alle Cluster zu allen Files "
  965.     "anzeigen (schließt -n ein)");
  966.   puts("\t-a: Alle mehrfach belegten Cluster "
  967.     "einer Datei anzeigen");
  968.   puts("\t-x: Bei mehrfach belegten Clustern "
  969.     "auch die anderen Dateinamen melden");
  970.   puts("\t-l: Komplette Pfadnamen bei -x melden "
  971.     "(schließt -x ein, hoher Speicher-\n"
  972.     "\t    bedarf!)");
  973.   puts("\t-d: Bei der Ergebnisausgabe als "
  974.     "defekt markierte Cluster melden");
  975.   puts("\t-1: Bei Ungleichheit FAT 1 als "
  976.     "Prüfgrundlage benutzen");
  977.   puts("\t-f: Fragmentierungsinformationen "
  978.     "ausgeben");
  979.   puts("\t-u: Laufwerk nicht mit Dlock sperren");
  980.   puts("\t-s: Keine unnötigen Ausgaben");
  981.   puts("\t-h: Nach Programmende auf Tastendruck "
  982.     "warten");
  983.   puts("\t-?: Dieser Hilfetext");
  984. }
  985.  
  986. /*
  987.  * work_dir
  988.  *
  989.  * Bearbeitet alle Files eines Verzeichnisses und
  990.  * alle Unterverzeichnisse. Gefunden werden dabei
  991.  * Clusterschleifen, Ordnerschleifen, illegale
  992.  * Start- und Folgecluster sowie Files mit
  993.  * defekten Clustern. Außerdem werden alle
  994.  * belegten Cluster vermerkt, um später mehrfach
  995.  * vergebene und verwaiste Cluster erkennen zu
  996.  * können.
  997.  *
  998.  * Eingabe:
  999.  * dir: Zeiger auf das zu bearbeitende Verzeichnis
  1000.  * entries: Maximale Anzahl an Einträgen in dir
  1001.  * path: Pfadname von dir (mit abschließendem \\)
  1002.  * scl: Array mit Anfangsclustern der
  1003.  *      "Elternverzeichnisse" von dir
  1004.  * depth: Bisherige Schachtelungstiefe, 0 =
  1005.  *        Wurzelverzeichnis. scl enthält somit
  1006.  *        depth Einträge.
  1007.  */
  1008. void work_dir(DIR *dir, WORD entries, char *path,
  1009.   UWORD *scl, WORD depth)
  1010. {
  1011.   WORD  i, j, k,
  1012.         frag,
  1013.         dist,
  1014.         first;
  1015.   UWORD cl,
  1016.         lastcl;
  1017.   ULONG clusts,
  1018.         must;
  1019.   DIR   *subdir;
  1020.   char  my_path[PATHMAX],
  1021.         fname[13];
  1022.  
  1023. /*
  1024.  * Sollte maximale Schachtelungstiefe erreicht
  1025.  * sein, Funktion abbrechen und Fehler melden
  1026.  */
  1027.   if (depth == MAXDEPTH)
  1028.   {
  1029.     if (!shownames)
  1030.       printf("%s: ", path);
  1031.     puts("Pfad zu tief verschachtelt!\n");
  1032.     quit = TOODEEP;
  1033.     return;
  1034.   }
  1035. /* Einträge des Verzeichnisses umwandeln */
  1036.   correct_dir(dir, entries);
  1037. /*
  1038.  * Prüfen, ob die beiden ersten Einträge . und ..
  1039.  * heissen und Verzeichnisse sind. Wenn nicht,
  1040.  * Verzeichnis nicht bearbeiten. Dieser Test muß
  1041.  * natürlich im Wurzelverzeichnis entfallen.
  1042.  */
  1043.   if (depth)
  1044.   {
  1045.     if (strncmp(dir[0].dir_name, ".          ",
  1046.       11) || strncmp(dir[1].dir_name,
  1047.       "..         ", 11) ||
  1048.       !(dir[0].dir_attr & 16) ||
  1049.       !(dir[1].dir_attr & 16))
  1050.     {
  1051.       if (!shownames)
  1052.         printf("%s: ", path);
  1053.       puts("Kein '.' und/oder '..'!");
  1054.       to_report[ILLSUBDIR]++;
  1055.       return;
  1056.     }
  1057. /*
  1058.  * Jetzt noch testen, ob . und .. die richtigen
  1059.  * Startcluster haben. Falls nicht, wird dies
  1060.  * gemeldet, das Verzeichnis aber trotzdem
  1061.  * bearbeitet.
  1062.  */
  1063.     if (dir[0].dir_stcl != scl[depth - 1])
  1064.     {
  1065.       if (!shownames)
  1066.         printf("%s: ", path);
  1067.       puts("Falscher Startcluster für '.'!");
  1068.       to_report[ILLPSEUDO]++;
  1069.     }
  1070.     if (dir[1].dir_stcl != ((depth == 1) ? 0 :
  1071.       scl[depth - 2]))
  1072.     {
  1073.       if (!shownames)
  1074.         printf("%s: ", path);
  1075.       puts("Falscher Startcluster für '..'!");
  1076.       to_report[ILLPSEUDO]++;
  1077.     }
  1078.   }
  1079. /* Alle Einträge durchgehen */
  1080.   for (i = (depth) ? 2 : 0; i < entries; i++)
  1081.   {
  1082. /*
  1083.  * Ist das erste Zeichen des aktuellen Filenamens
  1084.  * eine Null, ist der letzte Eintrag erreicht
  1085.  */
  1086.     if (!dir[i].dir_name[0])
  1087.       break;
  1088. /*
  1089.  * Eintrag nur überprüfen, wenn es kein gelöschtes
  1090.  * File und kein Laufwerkslabel ist
  1091.  */
  1092.     if ((dir[i].dir_name[0] == (char)0xe5) ||
  1093.       (dir[i].dir_attr & 8))
  1094.     {
  1095.       continue;
  1096.     }
  1097.     frag = 0;
  1098. /*
  1099.  * Startcluster ermitteln und aktuelle Filenummer
  1100.  * erhöhen. Durch diese Nummer erhält jedes File
  1101.  * eine individuelle Kennzeichnung, mit der es
  1102.  * möglich ist, Clusterschleifen zuverlässig zu
  1103.  * erkennen. Da filenr ein 16-Bit-Wert ist, darf
  1104.  * ein Laufwerk maximal 65535 Dateien/Ordner
  1105.  * beherbergen; allerdings ist diese Grenze schon
  1106.  * technisch nicht erreichbar, da eine 16-Bit-Fat
  1107.  * maximal 32766 Dateicluster haben kann. Außerdem
  1108.  * ermöglicht es diese individuelle Nummer, bei
  1109.  * bereits belegten Clustern zu ermitteln, zu
  1110.  * welcher Datei sie gehören.
  1111.  */
  1112.     cl = dir[i].dir_stcl;
  1113.     if ((filenr++) == 32768U)
  1114.     {
  1115.       quit = TMFILES;
  1116.       return;
  1117.     }
  1118. /* Den aktuellen Filenamen basteln */
  1119.     for (j = 0; j < 8; j++)
  1120.     {
  1121.       if (dir[i].dir_name[j] != ' ')
  1122.         fname[j] = dir[i].dir_name[j];
  1123.       else
  1124.         break;
  1125.     }
  1126.     fname[j] = 0;
  1127.     if (dir[i].dir_name[8] != ' ')
  1128.     {
  1129.       fname[j++] = '.';
  1130.       for (k = 0; k < 3; k++)
  1131.       {
  1132.         if (dir[i].dir_name[8 + k] != ' ')
  1133.           fname[j + k] = dir[i].dir_name[8 + k];
  1134.         else
  1135.           break;
  1136.       }
  1137.       fname[j + k] = 0;
  1138.     }
  1139.     if (dir[i].dir_attr & 16)
  1140.     {
  1141. /*
  1142.  * Eintrag ist Ordner, also neuen Pfadnamen
  1143.  * zusammensetzen; dabei vorher prüfen, ob my_path
  1144.  * noch genügend Platz bietet; wenn nicht, wird
  1145.  * TOODEEP gemeldet
  1146.  */
  1147.       if ((strlen(path) + 2 + strlen(fname)) >
  1148.         PATHMAX)
  1149.       {
  1150.         printf("%s%s\\ ist zu lang!\n", path,
  1151.           fname);
  1152.         quit = TOODEEP;
  1153.         return;
  1154.       }
  1155.       strcpy(my_path, path);
  1156.       strcat(my_path, fname);
  1157.       strcat(my_path, "\\");
  1158.       if (shownames)
  1159.       {
  1160.         printf("%s ", my_path);
  1161.         if (showclusters)
  1162.           printf("%u ", cl);
  1163.       }
  1164. /*
  1165.  * Startcluster überprüfen und notfalls
  1166.  * beanstanden. Bei Ordnern wird auch Null als
  1167.  * Startcluster beanstandet, da Ordner nie
  1168.  * komplett leer sein dürfen (sie müssen
  1169.  * mindestens . und .. enthalten).
  1170.  */
  1171.       if ((cl < 2) || (cl > MAXCLUST))
  1172.       {
  1173.         if (!shownames)
  1174.           printf("%s: ", my_path);
  1175.         puts("Illegaler Startcluster!");
  1176.         to_report[ILLEGAL]++;
  1177.         continue;
  1178.       }
  1179. /*
  1180.  * Prüfen, ob dieser Startcluster im aktuellen
  1181.  * Pfad schon einmal vorhanden war. Wenn ja, zeigt
  1182.  * das neue Verzeichnis auf einen seiner
  1183.  * Vorgänger, es gibt also eine Ordnerschleife.
  1184.  */
  1185.       for (j = 0; j < depth; j++)
  1186.       {
  1187.         if (scl[j] == cl)
  1188.         {
  1189.           if (!shownames)
  1190.             printf("%s: ", my_path);
  1191.           puts("Ordnerschleife!");
  1192.           to_report[DIRLOOPS]++;
  1193.           break;
  1194.         }
  1195.       }
  1196.       if (j != depth)
  1197.         continue;
  1198. /*
  1199.  * Bei aktiver Kommandozeilenoption -x den Ordner-
  1200.  * bzw. Pfadnamen speichern. Ist dazu nicht mehr
  1201.  * genug Speicher vorhanden, wird work_dir
  1202.  * abgebrochen. Die Speicherung erfolgt erst
  1203.  * jetzt, weil vorher noch nicht sichergestellt
  1204.  * war, ob der Ordner überhaupt gültig ist, was
  1205.  * unter Umständen Speicher spart.
  1206.  */
  1207.       if (crosslinks)
  1208.       {
  1209.         if (longnames)
  1210.         {
  1211.           filenames[filenr] =
  1212.             malloc(strlen(my_path) + 1);
  1213.         }
  1214.         else
  1215.         {
  1216.           filenames[filenr] =
  1217.             malloc(strlen(fname) + 1);
  1218.         }
  1219.         if (filenames[filenr] == 0L)
  1220.         {
  1221.           if (shownames)
  1222.             puts("");
  1223.           quit = NOMEM;
  1224.           return;
  1225.         }
  1226.         if (longnames)
  1227.           strcpy(filenames[filenr], my_path);
  1228.         else
  1229.         {
  1230.           strcpy(filenames[filenr], fname);
  1231.           strcat(filenames[filenr], "\\");
  1232.         }
  1233.       }
  1234. /*
  1235.  * Den Startcluster in der Liste vermerken und
  1236.  * Speicher für ein neues Verzeichnis anfordern
  1237.  */
  1238.       scl[depth] = cl;
  1239.       if ((subdir = (DIR *)malloc(clsiz_b)) != 0L)
  1240.       {
  1241. /*
  1242.  * War Speicher vorhanden, alle Cluster des neuen
  1243.  * Directories ermitteln und prüfen
  1244.  */
  1245.         clusts = first = 0;
  1246.         while (cl < 0xfff0U)
  1247.         {
  1248. /* Testen, ob aktueller Cluster gültig ist */
  1249.           if ((cl < 2) || ((cl > MAXCLUST) &&
  1250.             (cl < 0xfff0)))
  1251.           {
  1252.             if (!shownames)
  1253.               printf("%s: ", my_path);
  1254.             puts("Illegaler Folgecluster!");
  1255.             to_report[ILLEGAL]++;
  1256.             break;
  1257.           }
  1258. /*
  1259.  * Testen, ob der Cluster überhaupt belegt ist;
  1260.  * falls nicht, trotzdem einlesen
  1261.  */
  1262.           if (!fat[cl])
  1263.           {
  1264.             if (!shownames)
  1265.               printf("%s: ", my_path);
  1266.             puts("Enthält leeren Cluster!");
  1267.             to_report[EMPTYCLUST]++;
  1268.           }
  1269. /*
  1270.  * Gehört der aktuelle Cluster bereits zu diesem
  1271.  * Ordner, liegt eine Clusterschleife vor
  1272.  */
  1273.           if (tempcheck[cl] == filenr)
  1274.           {
  1275.             if (!shownames)
  1276.               printf("%s: ", my_path);
  1277.             puts("Clusterschleife!");
  1278.             to_report[LOOPS]++;
  1279.             break;
  1280.           }
  1281. /*
  1282.  * War der Cluster bereits belegt, dies (ggf. mit
  1283.  * File- bzw. Pfadnamen der Datei, die ihn belegt)
  1284.  * ausgeben (je nach Einstellung von showall
  1285.  * nur, wenn es der erste Cluster war). Dabei wird
  1286.  * die Einleseschleife nicht abgebrochen, da das
  1287.  * Unterverzeichnis ja noch weitergeht (wenn auch
  1288.  * nicht mehr ganz korrekt).
  1289.  */
  1290.           if (tempcheck[cl])
  1291.           {
  1292.             if (showall || !first)
  1293.             {
  1294.               first = 1;
  1295.               if (!shownames)
  1296.                 printf("%s: ", my_path);
  1297.               if (crosslinks)
  1298.               {
  1299.                 printf("Cluster %d bereits durch "
  1300.                   "%s belegt!\n", cl,
  1301.                   filenames[tempcheck[cl]]);
  1302.               }
  1303.               else
  1304.               {
  1305.                 printf("Cluster %d bereits "
  1306.                   "belegt!\n", cl);
  1307.               }
  1308.             }
  1309.           }
  1310. /*
  1311.  * War alles OK, den aktuellen Cluster einlesen.
  1312.  * Tritt dabei ein Fehler auf, die Routine
  1313.  * abbrechen.
  1314.  */
  1315.           if (Rwabs(0, (void *)((LONG)subdir +
  1316.             clusts * clsiz_b),
  1317.             bpb->clsiz, bpb->datrec + (cl - 2) *
  1318.             bpb->clsiz, drive))
  1319.           {
  1320.             free((void *)subdir);
  1321.             if (shownames)
  1322.               puts("");
  1323.             quit = READERROR;
  1324.             break;
  1325.           }
  1326. /*
  1327.  * In der FAT-Testtabelle den Belegungsgrad des
  1328.  * aktuellen Clusters erhöhen und vermerken, daß
  1329.  * er bereits zu diesem Ordner gehört
  1330.  */
  1331.           checkfat[cl]++;
  1332.           tempcheck[cl] = filenr;
  1333. /*
  1334.  * Folgecluster ermitteln und ggf. neuen Speicher
  1335.  * anfordern
  1336.  */
  1337.           lastcl = cl;
  1338.           cl = fat[cl];
  1339.           clusts++;
  1340.           if (!cl)
  1341.             break;
  1342.           if (cl < 0xfff0U)
  1343.           {
  1344.             if (showclusters)
  1345.               printf("%u ", cl);
  1346. /* Clustersprung merken, falls nötig */
  1347.             if (showfrag && ((lastcl + 1) != cl))
  1348.             {
  1349.               if (!frag)
  1350.                 fragmented++;
  1351.               fragments++;
  1352.               if (verbose)
  1353.               {
  1354.                 dist = abs(lastcl - cl);
  1355.                 distances += dist;
  1356.                 if (dist < min_dist)
  1357.                   min_dist = dist;
  1358.                 if (dist > max_dist)
  1359.                   max_dist = dist;
  1360.               }
  1361.               frag = 1;
  1362.             }
  1363.             subdir = (DIR *)realloc((void *)
  1364.               subdir, (clusts + 1L) * clsiz_b);
  1365.             if (subdir == 0L)
  1366.             {
  1367.               quit = NOMEM;
  1368.               if (shownames)
  1369.                 puts("");
  1370.             }
  1371.           }
  1372.         }
  1373.         if (!quit)
  1374.         {
  1375. /*
  1376.  * Alle Cluster sind ermittelt. War der letzte
  1377.  * Cluster ein als defekt markierter, dieses
  1378.  * vermerken.
  1379.  */
  1380.           if ((cl >= 0xfff0) && (cl < 0xfff8U))
  1381.           {
  1382.             if (!shownames)
  1383.               printf("%s: ", my_path);
  1384.             puts("Enthält defekten Cluster!");
  1385.             to_report[DESTROYED]++;
  1386.           }
  1387.           if (shownames)
  1388.             puts("");
  1389. /*
  1390.  * work_dir rekursiv mit dem neu eingelesenen
  1391.  * Verzeichnis aufrufen
  1392.  */
  1393.           work_dir(subdir, (WORD)(clusts *
  1394.             clsiz_b / 32L), my_path, scl,
  1395.             depth + 1);
  1396.           free(subdir);
  1397.         }
  1398.       }
  1399.       else
  1400.       {
  1401.         quit = NOMEM;
  1402.         if (shownames)
  1403.           puts("");
  1404.       }
  1405.     }
  1406.     else
  1407.     {
  1408. /*
  1409.  * Aktueller Verzeichniseintrag ist eine Datei.
  1410.  * Auch hier zunächst den Startcluster prüfen.
  1411.  */
  1412.       if (shownames)
  1413.       {
  1414.         printf("%s%s ", path, fname);
  1415.         if (showclusters)
  1416.           printf("%u ", cl);
  1417.       }
  1418.       clusts = first = 0;
  1419. /*
  1420.  * Bei Dateien Startcluster Null nicht
  1421.  * beanstanden, es handelt sich hierbei um
  1422.  * reguläre Files mit Länge Null
  1423.  */
  1424.       if (!cl)
  1425.       {
  1426.         if (shownames)
  1427.           puts("");
  1428.         continue;
  1429.       }
  1430. /* Startcluster prüfen */
  1431.       if ((cl < 2) || (cl > MAXCLUST))
  1432.       {
  1433.         if (!shownames)
  1434.           printf("%s%s: ", path, fname);
  1435.         puts("Illegaler Startcluster!");
  1436.         to_report[ILLEGAL]++;
  1437.       }
  1438.       else
  1439.       {
  1440. /*
  1441.  * War Startcluster OK, aus der Dateilänge
  1442.  * berechnen, wieviele Cluster diese Datei
  1443.  * belegen muß. Danach alle Cluster des Files
  1444.  * durchgehen. Zuvor wird noch, falls nötig (-x),
  1445.  * der Datei- bzw. Pfadname gespeichert.
  1446.  */
  1447.         if (crosslinks)
  1448.         {
  1449.           if (longnames)
  1450.           {
  1451.             filenames[filenr] = malloc(
  1452.               strlen(path) + strlen(fname) + 1);
  1453.           }
  1454.           else
  1455.           {
  1456.             filenames[filenr] =
  1457.               malloc(strlen(fname) + 1);
  1458.           }
  1459.           if (filenames[filenr] == 0L)
  1460.           {
  1461.             quit = NOMEM;
  1462.             if (shownames)
  1463.               puts("");
  1464.             return;
  1465.           }
  1466.           if (longnames)
  1467.           {
  1468.             strcpy(filenames[filenr], path);
  1469.             strcat(filenames[filenr], fname);
  1470.           }
  1471.           else
  1472.             strcpy(filenames[filenr], fname);
  1473.         }
  1474.         must = (dir[i].dir_flen / clsiz_b);
  1475.         if (dir[i].dir_flen % clsiz_b)
  1476.           must++;
  1477.         while (cl < 0xfff0U)
  1478.         {
  1479. /* Cluster auf Gültigkeit prüfen */
  1480.           if ((cl < 2) || ((cl > MAXCLUST) &&
  1481.             (cl < 0xfff0)))
  1482.           {
  1483.             if (!shownames)
  1484.               printf("%s%s: ", path, fname);
  1485.             puts("Illegaler Folgecluster!");
  1486.             to_report[ILLEGAL]++;
  1487.             break;
  1488.           }
  1489. /*
  1490.  * Prüfen, ob der Cluster überhaupt belegt ist;
  1491.  * falls nicht, trotzdem mitzählen
  1492.  */
  1493.           if (!fat[cl])
  1494.           {
  1495.             if (!shownames)
  1496.               printf("%s%s: ", path, fname);
  1497.             puts("Enthält leeren Cluster!");
  1498.             to_report[EMPTYCLUST]++;
  1499.           }
  1500. /*
  1501.  * War der aktuelle Cluster schon durch diese
  1502.  * Datei belegt, Clusterschleife melden
  1503.  */
  1504.           if (tempcheck[cl] == filenr)
  1505.           {
  1506.             if (!shownames)
  1507.               printf("%s%s: ", path, fname);
  1508.             puts("Clusterschleife!");
  1509.             to_report[LOOPS]++;
  1510.             break;
  1511.           }
  1512. /*
  1513.  * Ist der Cluster bereits durch eine andere
  1514.  * Datei belegt, dieses melden. Die Schleife wird
  1515.  * dabei nicht abgebrochen, da die Datei dadurch
  1516.  * ja noch nicht beendet ist.
  1517.  */
  1518.           if (tempcheck[cl])
  1519.           {
  1520.             if (showall || !first)
  1521.             {
  1522.               first = 1;
  1523.               if (!shownames)
  1524.                 printf("%s%s: ", path, fname);
  1525.               if (crosslinks)
  1526.               {
  1527.                 printf("Cluster %d bereits durch "
  1528.                   "%s belegt!\n", cl,
  1529.                   filenames[tempcheck[cl]]);
  1530.               }
  1531.               else
  1532.               {
  1533.                 printf("Cluster %d bereits "
  1534.                   "belegt!\n", cl);
  1535.               }
  1536.             }
  1537.           }
  1538. /*
  1539.  * Jetzt Belegungsgrad des aktuellen Clusters
  1540.  * erhöhen und vermerken, daß er bereits in diesem
  1541.  * File benutzt wurde
  1542.  */
  1543.           checkfat[cl]++;
  1544.           tempcheck[cl] = filenr;
  1545. /*
  1546.  * Folgecluster ermitteln und Anzahl der durch das
  1547.  * File tatsächlich belegten Cluster um eins
  1548.  * erhöhen
  1549.  */
  1550.           lastcl = cl;
  1551.           cl = fat[cl];
  1552.           clusts++;
  1553.           if (!cl)
  1554.             break;
  1555.           if (cl < 0xfff0U)
  1556.           {
  1557.             if (showclusters)
  1558.               printf("%u ", cl);
  1559. /* Clustersprung merken, falls nötig */
  1560.             if (showfrag && ((lastcl + 1) != cl))
  1561.             {
  1562.               if (!frag)
  1563.                 fragmented++;
  1564.               fragments++;
  1565.               if (verbose)
  1566.               {
  1567.                 dist = abs(lastcl - cl);
  1568.                 distances += dist;
  1569.                 if (dist < min_dist)
  1570.                   min_dist = dist;
  1571.                 if (dist > max_dist)
  1572.                   max_dist = dist;
  1573.               }
  1574.               frag = 1;
  1575.             }
  1576.           }
  1577.         }
  1578. /*
  1579.  * Sind alle Cluster geprüft, testen, ob das Ende
  1580.  * der Verkettung durch einen defekten Cluster
  1581.  * markiert wurde. Wenn ja, dies vermerken.
  1582.  */
  1583.         if ((cl >= 0xfff0) && (cl < 0xfff8U))
  1584.         {
  1585.           if (!shownames)
  1586.             printf("%s%s: ", path, fname);
  1587.           puts("Enthält defekten Cluster!");
  1588.           to_report[DESTROYED]++;
  1589.         }
  1590.         else
  1591.         {
  1592. /*
  1593.  * Ansonsten prüfen, ob die Datei die richtige
  1594.  * Anzahl von Clustern belegt
  1595.  */
  1596.           if (clusts != must)
  1597.           {
  1598.             if (!shownames)
  1599.               printf("%s%s: ", path, fname);
  1600.             puts("Abweichende Längen!");
  1601.             to_report[WRONG]++;
  1602.             continue;
  1603.           }
  1604.         }
  1605.         if (shownames)
  1606.           puts("");
  1607.       }
  1608.     }
  1609.     if (quit)
  1610.       return;
  1611.   }
  1612. }
  1613.  
  1614. /*
  1615.  * correct_dir
  1616.  *
  1617.  * Wandelt die Einträge eines eingelesenen
  1618.  * Verzeichnisses vom Intel- in's Motorola-Format
  1619.  * um.
  1620.  *
  1621.  * Eingabe:
  1622.  * dir: Zeiger auf Verzeichnis
  1623.  * entries: Anzahl zu wandelnder Einträge in dir
  1624.  */
  1625. void correct_dir(DIR *dir, WORD entries)
  1626. {
  1627.   WORD  i;
  1628.   
  1629.   for (i = 0; i < entries; i++)
  1630.   {
  1631.     swap(&dir[i].dir_time);
  1632.     swap(&dir[i].dir_date);
  1633.     swap(&dir[i].dir_stcl);
  1634.     lswap(&dir[i].dir_flen);
  1635.   }
  1636. }
  1637.  
  1638. /*
  1639.  * swap
  1640.  *
  1641.  * 16-Bit-Wert vom Intel- in's Motorola-Format
  1642.  * umwandeln (und umgekehrt).
  1643.  *
  1644.  * Eingabe:
  1645.  * value: Zeiger auf zu wandelndes Wort
  1646.  */
  1647. void swap(UWORD *value)
  1648. {
  1649.   *value = ((*value & 255) << 8) + (*value >> 8);
  1650. }
  1651.  
  1652. /*
  1653.  * lswap
  1654.  *
  1655.  * Wie swap, jedoch für 32-Bit-Werte.
  1656.  *
  1657.  * Eingabe:
  1658.  * value: Zeiger auf zu wandelnden Long
  1659.  */
  1660. void lswap(ULONG *value)
  1661. {
  1662.   UWORD high, low;
  1663.   
  1664.   low = (UWORD)(*value & 65535L);
  1665.   high = (UWORD)(*value >> 16);
  1666.   
  1667.   swap(&low);
  1668.   swap(&high);
  1669.   
  1670.   *value = ((LONG)low << 16) + (LONG)high;
  1671. }
  1672.  
  1673. /*
  1674.  * err
  1675.  *
  1676.  * Fehlerbehandlungsroutine. Wird angesprungen,
  1677.  * wenn während des Programmablaufs ein Fehler
  1678.  * aufgetreten ist. Hier wird, wenn nötig, aller
  1679.  * angeforderter Speicher freigegeben und eine
  1680.  * Meldung ausgegeben, welcher Fehler aufgetreten
  1681.  * ist. Die Funktion kehrt nicht zurück, sondern
  1682.  * beendet das Programm.
  1683.  *
  1684.  * Eingabe:
  1685.  * code: Fehlercode
  1686.  */
  1687. void err(WORD code){
  1688.   WORD i;
  1689.  
  1690. /* Wenn nötig, Speicher freigeben */
  1691.   for (i = 32767; i >= 0; i--)
  1692.   {
  1693.     if (filenames[i])
  1694.       free(filenames[i]);
  1695.   }
  1696.   if (tempcheck != 0L)
  1697.     free((void *)tempcheck);
  1698.   if (checkfat != 0L)
  1699.     free((void *)checkfat);
  1700.   if (fat != 0L)
  1701.     free((void *)fat);
  1702.   if (rootdir != 0L)
  1703.     free((void *)rootdir);
  1704. /* Meldung ausgeben */
  1705.   printf("CheckFat: %s\n", errtext[code]);
  1706. /* Laufwerk freigeben, wenn nötig */
  1707.   if (!nolock && (lock == 0L))
  1708.     Dlock(0, drive);
  1709. /* Und tschüß... */
  1710.   leave(1);
  1711. }
  1712.  
  1713. /* EOF */
  1714.